bookwiz.io / app / api / books / [id] / semantic-search / route.ts
route.ts
Raw
import { NextRequest, NextResponse } from 'next/server'
import { semanticIndexingService } from '@/lib/services/semantic-indexing-service'
import { createServerSupabaseClient } from '@/lib/supabase'

// Create server-side Supabase client with user session
function createSupabaseClient(request: Request) {
  // Get the authorization header from the request
  const authHeader = request.headers.get('authorization')
  
  return createServerSupabaseClient()
}

export async function POST(
  req: NextRequest,
  { params }: { params: { id: string } }
) {
  try {
    const { query, maxResults = 10, similarityThreshold = 0.01 } = await req.json()
    
    if (!query) {
      return NextResponse.json({ error: 'Query is required' }, { status: 400 })
    }

    const bookId = params.id
    const supabase = createSupabaseClient(req)

    // Get current user session from auth header
    const authHeader = req.headers.get('authorization')
    let user
    
    try {
      if (authHeader) {
        const { data: { user: authUser }, error: authError } = await supabase.auth.getUser(authHeader.replace('Bearer ', ''))
        if (!authError) user = authUser
      } else {
        const { data: { user: sessionUser }, error: sessionError } = await supabase.auth.getUser()
        if (!sessionError) user = sessionUser
      }
    } catch (e) {
      // Ignore auth errors
    }

    if (!user) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
    }

    // Verify user has access to this book
    const { data: book } = await supabase
      .from('books')
      .select('id')
      .eq('id', bookId)
      .eq('user_id', user.id)
      .single()

    if (!book) {
      return NextResponse.json({ error: 'Book not found' }, { status: 404 })
    }

    // Perform semantic search
    const results = await semanticIndexingService.semanticSearch(
      bookId,
      query,
      { maxResults, similarityThreshold }
    )

    return NextResponse.json({ results })
  } catch (error) {
    console.error('Semantic search error:', error)
    return NextResponse.json(
      { error: 'Failed to perform semantic search' },
      { status: 500 }
    )
  }
}

export async function GET(
  req: NextRequest,
  { params }: { params: { id: string } }
) {
  try {
    const bookId = params.id
    const supabase = createSupabaseClient(req)

    // Get current user session from auth header
    const authHeader = req.headers.get('authorization')
    let user
    
    try {
      if (authHeader) {
        const { data: { user: authUser }, error: authError } = await supabase.auth.getUser(authHeader.replace('Bearer ', ''))
        if (!authError) user = authUser
      } else {
        const { data: { user: sessionUser }, error: sessionError } = await supabase.auth.getUser()
        if (!sessionError) user = sessionUser
      }
    } catch (e) {
      // Ignore auth errors
    }

    if (!user) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
    }

    // Verify user has access to this book
    const { data: book } = await supabase
      .from('books')
      .select('id')
      .eq('id', bookId)
      .eq('user_id', user.id)
      .single()

    if (!book) {
      return NextResponse.json({ error: 'Book not found' }, { status: 404 })
    }

    // Get indexing status
    const { data: statusData } = await supabase
      .from('file_embedding_status')
      .select(`
        file_id,
        status,
        last_processed_at,
        error_message,
        file_system_items!inner(name, type)
      `)
      .eq('book_id', bookId)

    const stats = {
      totalFiles: 0,
      pendingFiles: 0,
      processingFiles: 0,
      completedFiles: 0,
      failedFiles: 0,
      files: statusData || []
    }

    statusData?.forEach((item: any) => {
      stats.totalFiles++
      switch (item.status) {
        case 'pending':
        case 'stale':
          stats.pendingFiles++
          break
        case 'processing':
          stats.processingFiles++
          break
        case 'completed':
          stats.completedFiles++
          break
        case 'failed':
          stats.failedFiles++
          break
      }
    })

    return NextResponse.json({ stats })
  } catch (error) {
    console.error('Get indexing status error:', error)
    return NextResponse.json(
      { error: 'Failed to get indexing status' },
      { status: 500 }
    )
  }
}